# -*- encoding: utf-8 -*-
"""
Settings
========
THUMBS_GENERATE_THUMBNAILS
Generate thumbnails when saving objects.
Default: True

THUMBS_GENERATE_MISSING_THUMBNAILS
Generate thumbnail when its url is accessed and the the file doesn't exist.
Set this option when you are replacing ImageField with ImageWithThumbsField on
a populated database where the thumbnails doesn't exist.
Default: True

THUMBS_GENERATE_ANY_SIZE
Generate the thumbnail even if it's not in the configured `sizes` argument.
Default: False
"""
import io
import os

from PIL import Image

from django.db.models import ImageField
from django.db.models.fields.files import ImageFieldFile
from django.core.files.base import ContentFile
from django.conf import settings


THUMBS_GENERATE_THUMBNAILS = getattr(
    settings, "THUMBS_GENERATE_THUMBNAILS", True)
THUMBS_GENERATE_MISSING_THUMBNAILS = getattr(
    settings, "THUMBS_GENERATE_MISSING_THUMBNAILS", True)
THUMBS_GENERATE_ANY_SIZE = getattr(
    settings, "THUMBS_GENERATE_ANY_SIZE", False)


class ImageWithThumbsFieldFile(ImageFieldFile):
    """
    Django `ImageField` replacement with automatic generation of thumbnail
    images. See `ImageWithThumbsField` for usage example.
    """

    THUMBS_DIR = 'thumbs'

    def __init__(self, *args, **kwargs):
        super(ImageFieldFile, self).__init__(*args, **kwargs)

    def _make_thumb(self, image, size=300, format_='JPEG'):
        image = Image.open(image)
        width, height = image.size

        if width > height:
            delta = height / size
            width = int(width / delta)
            image.thumbnail((width, size), Image.ANTIALIAS)
        else:
            delta = width / size
            height = int(height / delta)
            image.thumbnail((size, height), Image.ANTIALIAS)

        im = image
        thumb_io = io.BytesIO()
        if im.mode != 'RGB':
            im = im.convert('RGB')
        if format_.upper() == 'JPG':
            format_ = 'JPEG'
        im.save(thumb_io, format=format_)
        name = os.path.basename(self.name)
        thumb = ContentFile(thumb_io.getvalue(), name=name)
        return thumb

    def _url_for_size(self, size):
        """
        Return a URL pointing to the thumbnail image of the requested size.
        If `THUMBS_GENERATE_MISSING_THUMBNAILS` is True, the thumbnail will be
        created if it doesn't exist on disk.

        Arguments:
        size  -- A int of size. Example: 100
        """
        try:
            thumb_file = self._get_key_by_key(self.name, size)
            # generate missing thumbnail if needed
            if THUMBS_GENERATE_MISSING_THUMBNAILS:
                if not self.storage.exists(thumb_file):
                    image = self.storage.open(self.name)
                    self._generate_thumb(image, size)

            return self.storage.url(thumb_file)
        except Exception as e:
            return  self.url


    def __getattr__(self, name):
        """
        Return the url for the requested size.

        Arguments:
        name -- The field `url` with size suffix formatted as _size.
        Example: instance.url_100
        """

        if not name.startswith('url_'):
            return super(ImageWithThumbsFieldFile, self).__getattr__(name)

        size_str = name.replace("url_", "")
        requested_size = int(size_str)

        accepted_size = None
        if THUMBS_GENERATE_ANY_SIZE:
            accepted_size = requested_size
        else:
            if requested_size in self.field.sizes:
                accepted_size = requested_size
        if accepted_size:
            return self._url_for_size(accepted_size)

        raise ValueError(f"The requested thumbnail size `{size_str}` "
                         f"doesn't in `sizes`{self.field.sizes}")

    def _get_key_by_key(self, obj_key, size):
        """
        Arguments:
        obj_key -- The key of original image object in COS.
        size -- min(width, height). Example: 100

        Return:
        the key of thumb image object in COS.
        """
        basename = os.path.basename(obj_key)
        return self._get_key_by_name(basename, size=size)

    def _get_key_by_name(self, filename, size=None):
        """
        Arguments:
        filename -- Like "any_filename.jpg".
        size -- min(width, height). Example: 100

        Return:
        the key of thumb image object in COS.
        """
        base, extension = filename.rsplit('.', 1)
        thumb_dir = self.THUMBS_DIR if size else ''
        size = f'.{size}' if size else ''
        return f'{self.field.upload_to}{thumb_dir}/{base}{size}.{extension}'

    def _generate_thumb(self, image, size):
        """
        Generates a thumbnail of `size`.

        Arguments:
        image -- An `File` object with the image in its original size. If
            `None` is given, this func will get file by `obj_key` instead.
        size -- min(width, height). Example: 100
        """
        thumb_key = self._get_key_by_key(self.name, size)
        if self.storage.exists(thumb_key):
            return

        extension = self.name.split('.')[-1]
        thumbnail = self._make_thumb(image, size=size, format_=extension)

        self.storage.save(thumb_key, thumbnail)

    def save(self, name, content, save=True):
        if THUMBS_GENERATE_THUMBNAILS:
            if self.field.sizes:
                for size in self.field.sizes:
                    self._generate_thumb(content, size)
        super(ImageFieldFile, self).save(name, content, save)

    def delete(self, save=True):
        # todo: obj.photo.delete()会调用此方法，删除后同步删除COS中的对象尚未实现
        if self.name and self.field.sizes:
            for size in self.field.sizes:
                thumb_name = self._get_key_by_key(self.name, size)
                if self.storage.exists(thumb_name):
                    self.storage.delete(thumb_name)
        super(ImageFieldFile, self).delete(save=save)

    def generate_thumbnails(self):
        if self.field.sizes:
            image = self.storage.open(self.name)
            for size in self.field.sizes:
                self._generate_thumb(image, size)

    def thumbnail(self, size):
        """
        Return the thumbnail url for an specific size. The same thing
        as url_[size] without the magic.

        Usage:
        instance.thumbnail(64)
        """
        return self.__getattr__(f'url_{size}')


class ImageWithThumbsField(ImageField):
    """
    Usage example:
    ==============
    photo = ImageWithThumbsField(upload_to='images', sizes=(125, 200, )

    To retrieve image URL, exactly the same way as with ImageField:
        my_object.photo.url
    To retrieve thumbnails URL's just add the size to it:
        my_object.photo.url_125
        my_object.photo.url_300

    Note: The 'sizes' attribute is not required. If you don't provide it,
    ImageWithThumbsField will act as a normal ImageField

    How it works:
    =============
    For each size in the 'sizes' atribute of the field it generates a
    thumbnail with that size and stores it following this format:

    available_filename.[size].extension

    Where 'available_filename' is the available filename returned by the
    storage backend for saving the original file.

    Following the usage example above: For storing a file called "photo.jpg"
    it saves:
    photo.jpg          (original file)
    photo.125.jpg      (first thumbnail)
    photo.200.jpg      (second thumbnail)

    With the default storage backend if photo.jpg already exists it will use
    these filenames:
    photo_.jpg
    photo_.125.jpg
    photo_.200.jpg

    Note:
    1. django-thumbs assumes that if filename "any_filename.jpg" is
    available filenames with this format "any_filename.[size].jpg" will be
    available, too.
    2. If "any_filename.jpg" already exists in COS when saving. This modular
    will just save the original file to COS like "any_filename_df23e2if.jpg",
    even if `THUMBS_GENERATE_THUMBNAILS` is True. But don't worry, the
    thumbnail will be generated automatically when you need to use at the
    first time if `THUMBS_GENERATE_MISSING_THUMBNAILS` is True.
    """
    attr_class = ImageWithThumbsFieldFile

    def __init__(self, verbose_name=None, name=None, width_field=None,
                 height_field=None, sizes=None, **kwargs):
        self.verbose_name = verbose_name
        self.name = name
        self.width_field = width_field
        self.height_field = height_field
        self.sizes = sizes
        super(ImageField, self).__init__(**kwargs)
